home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / smaltalk.lha / smalltalk-1.1.1 / mstlex.c < prev    next >
C/C++ Source or Header  |  1991-09-13  |  25KB  |  1,072 lines

  1. /***********************************************************************
  2.  *
  3.  *    Lexer Module.
  4.  *
  5.  ***********************************************************************/
  6.  
  7. /***********************************************************************
  8.  *
  9.  * Copyright (C) 1990, 1991 Free Software Foundation, Inc.
  10.  * Written by Steve Byrne.
  11.  *
  12.  * This file is part of GNU Smalltalk.
  13.  *
  14.  * GNU Smalltalk is free software; you can redistribute it and/or modify it
  15.  * under the terms of the GNU General Public License as published by the Free
  16.  * Software Foundation; either version 1, or (at your option) any later 
  17.  * version.
  18.  * 
  19.  * GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
  20.  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
  21.  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  22.  * more details.
  23.  * 
  24.  * You should have received a copy of the GNU General Public License along with
  25.  * GNU Smalltalk; see the file COPYING.  If not, write to the Free Software
  26.  * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
  27.  *
  28.  ***********************************************************************/
  29.  
  30. /*
  31.  *    Change Log
  32.  * ============================================================================
  33.  * Author      Date       Change 
  34.  * sbb         13 Sep 91      Fixed character pushback to properly deal with 2
  35.  *              characters of pushback.
  36.  *
  37.  * sbyrne    24 Apr 90      Error checking for integers too large.
  38.  *
  39.  * sbyrne    20 Apr 90      Added the closeIt argument to popStream so that the
  40.  *              closing behavior could be separated from the popping
  41.  *              behavior (in particular, for fileIn).
  42.  *
  43.  * sbyrne     7 Apr 90      Character lexing routines (such as nextChar) now
  44.  *              return ints to get around problems on implementations
  45.  *              that don't sign extend characters by default.
  46.  *
  47.  * sbyrne    15 Feb 90      Added support for := as alternative assignment
  48.  *              operator.
  49.  *
  50.  * sbyrne     3 Sep 89      added getCurFileName
  51.  *
  52.  * sbyrne    30 Aug 89      Fixed a bug in parseIdent which was parsing foo:2
  53.  *              (note no space) not as foo: and 2, but as a mixed up
  54.  *              token.
  55.  *
  56.  * sbyrne     8 Jul 89      Added prompt when input is a terminal.  This should
  57.  *              help Emacs's shell mode determine what has been typed
  58.  *              as input to the system.
  59.  *
  60.  * sbyrne    24 Jan 89      Added 2 chars of push back, because 3. needs to look
  61.  *              ahead one more character to see if its 3.DIGIT or 3.
  62.  *              next statement.
  63.  *
  64.  * sbyrne    27 Dec 88    Created.
  65.  */
  66.  
  67.  
  68. #include "mst.h"
  69. #include "mstlex.h"
  70. #include "mst.tab.h"
  71. #include "mststr.h"
  72. #include "mstid.h"
  73. #include "mstdict.h"
  74. #include "mstcomp.h"
  75. #include "msttree.h"
  76. #include <stdio.h>
  77. #include <math.h>
  78. #ifdef USE_READLINE
  79. #include <readline/readline.h>
  80. #endif /* USE_READLINE */
  81.  
  82. #define WHITE_SPACE        1
  83. #define DIGIT            2
  84. #define ID_CHAR            4
  85. #define SPECIAL_CHAR        8
  86.  
  87. #define ERROR_ARGS        arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8
  88.  
  89. #define EMACS_PROCESS_MARKER    '\001' /* ^A as marker -- random choice */
  90.  
  91. typedef struct StringStreamStruct {
  92.   char        *strBase;    /* base of asciz string */
  93.   char        *str;        /* pointer into asciz string */
  94. } StringStream;
  95.  
  96. struct StreamStruct {
  97.   StreamType    type;
  98.   int        pushedBackChars[2]; /* holds the 2 characters of buffering */
  99.   int        pushedBackCount; /* number of chars pushed back */
  100.   int        line;
  101.   int        column;
  102.   char        *fileName;
  103.   Boolean    prompt;
  104.   union {
  105.     FILE    *u_st_file;
  106.     StringStream u_st_str;
  107.   } st;
  108.   Stream    prevStream;
  109. };
  110.  
  111. #define st_file        st.u_st_file
  112. #define st_str        st.u_st_str
  113.  
  114. Stream    inStream = NULL;
  115. int    lexDebug;
  116. char    *processingFile = nil;
  117.  
  118. static int            byteAddr, methodStartPos = 0;
  119. static Boolean            produceInternalToken;
  120.  
  121. static Boolean            isDigit(), isBaseDigit(), parsePrimitive(),
  122.                 parseDigits(), parseFraction();
  123. static int            illegal(), comment(), charLiteral(),
  124.                 parseBinOP(), stringLiteral(), parseNumber(),
  125.                 parseIdent(), myGetC(), parseColon(),
  126.                   nextChar();
  127. static char            *scanStringoid();
  128. static void             unreadChar(), lineStamp(), myClose();
  129. static Stream            pushNewStream();
  130.  
  131. typedef struct {
  132.   int        (*lexFunc)();
  133.   int        retToken;
  134.   int        charClass;
  135. } LexTabElt;
  136.  
  137. static LexTabElt charTab[128] = {
  138. /*   0 */    illegal,    0,    0,
  139. /*   1 */    illegal,    0,    0,
  140. /*   2 */    illegal,    0,    0,
  141. /*   3 */    illegal,    0,    0,
  142. /*   4 */    illegal,    0,    0,
  143. /*   5 */    illegal,    0,    0,
  144. /*   6 */    illegal,    0,    0,
  145. /*   7 */    illegal,    0,    0,
  146. /*   8 */    illegal,    0,    0,
  147. /*   9 */    0,        0,    WHITE_SPACE,
  148. /*  10 */    0,        0,    WHITE_SPACE,
  149. /*  11 */    illegal,    0,    0,
  150. /*  12 */    0,        0,    WHITE_SPACE, 
  151. /*  13 */    0,        0,    WHITE_SPACE,
  152. /*  14 */    illegal,    0,    0,    
  153. /*  15 */    illegal,    0,    0,
  154. /*  16 */    illegal,    0,    0,
  155. /*  17 */    illegal,    0,    0,
  156. /*  18 */    illegal,    0,    0,
  157. /*  19 */    illegal,    0,    0,
  158. /*  20 */    illegal,    0,    0,
  159. /*  21 */    illegal,    0,    0,
  160. /*  22 */    illegal,    0,    0,
  161. /*  23 */    illegal,    0,    0,
  162. /*  24 */    illegal,    0,    0,
  163. /*  25 */    illegal,    0,    0,
  164. /*  26 */    illegal,    0,    0,
  165. /*  27 */    illegal,    0,    0,
  166. /*  28 */    illegal,    0,    0,
  167. /*  29 */    illegal,    0,    0,
  168. /*  30 */    illegal,    0,    0,
  169. /*  31 */    illegal,    0,    0,
  170. /*     */    0,        0,    WHITE_SPACE,
  171. /*   ! */    parseBinOP,    0,    SPECIAL_CHAR, 
  172. /*   " */    comment,    0,    0,
  173. /*   # */    0,        SHARP,    0,
  174. /*   $ */    charLiteral,    0,    0,
  175. /*   % */    parseBinOP,    0,    SPECIAL_CHAR,
  176. /*   & */    parseBinOP,    0,    SPECIAL_CHAR,
  177. /*   ' */    stringLiteral,    0,    0,
  178. /*   ( */    0,        OPEN_PAREN, 0,
  179. /*   ) */    0,        CLOSE_PAREN, 0,
  180. /*   * */    parseBinOP,    0,    SPECIAL_CHAR,
  181. /*   + */    parseBinOP,    0,    SPECIAL_CHAR,
  182. /*   , */    parseBinOP,    0,    SPECIAL_CHAR,
  183. /*   - */    parseBinOP,    0,    SPECIAL_CHAR,
  184. /*   . */    0,        DOT,    0,
  185. /*   / */    parseBinOP,    0,    SPECIAL_CHAR,
  186. /*   0 */    parseNumber,    0,    DIGIT | ID_CHAR,
  187. /*   1 */    parseNumber,    0,    DIGIT | ID_CHAR,
  188. /*   2 */    parseNumber,    0,    DIGIT | ID_CHAR,
  189. /*   3 */    parseNumber,    0,    DIGIT | ID_CHAR,
  190. /*   4 */    parseNumber,    0,    DIGIT | ID_CHAR,
  191. /*   5 */    parseNumber,    0,    DIGIT | ID_CHAR,
  192. /*   6 */    parseNumber,    0,    DIGIT | ID_CHAR,
  193. /*   7 */    parseNumber,    0,    DIGIT | ID_CHAR,
  194. /*   8 */    parseNumber,    0,    DIGIT | ID_CHAR,
  195. /*   9 */    parseNumber,    0,    DIGIT | ID_CHAR,
  196. /*   : */    parseColon,    0,    0,
  197. /*   ; */    0,        SEMICOLON, 0,
  198. /*   < */    parseBinOP,    0,    SPECIAL_CHAR,
  199. /*   = */    parseBinOP,    0,    SPECIAL_CHAR,
  200. /*   > */    parseBinOP,    0,    SPECIAL_CHAR,
  201. /*   ? */    parseBinOP,    0,    SPECIAL_CHAR,
  202. /*   @ */    parseBinOP,    0,    SPECIAL_CHAR,
  203. /*   A */    parseIdent,    0,    ID_CHAR,
  204. /*   B */    parseIdent,    0,    ID_CHAR,
  205. /*   C */    parseIdent,    0,    ID_CHAR,
  206. /*   D */    parseIdent,    0,    ID_CHAR,
  207. /*   E */    parseIdent,    0,    ID_CHAR,
  208. /*   F */    parseIdent,    0,    ID_CHAR,
  209. /*   G */    parseIdent,    0,    ID_CHAR,
  210. /*   H */    parseIdent,    0,    ID_CHAR,
  211. /*   I */    parseIdent,    0,    ID_CHAR,
  212. /*   J */    parseIdent,    0,    ID_CHAR,
  213. /*   K */    parseIdent,    0,    ID_CHAR,
  214. /*   L */    parseIdent,    0,    ID_CHAR,
  215. /*   M */    parseIdent,    0,    ID_CHAR,
  216. /*   N */    parseIdent,    0,    ID_CHAR,
  217. /*   O */    parseIdent,    0,    ID_CHAR,
  218. /*   P */    parseIdent,    0,    ID_CHAR,
  219. /*   Q */    parseIdent,    0,    ID_CHAR,
  220. /*   R */    parseIdent,    0,    ID_CHAR,
  221. /*   S */    parseIdent,    0,    ID_CHAR,
  222. /*   T */    parseIdent,    0,    ID_CHAR,
  223. /*   U */    parseIdent,    0,    ID_CHAR,
  224. /*   V */    parseIdent,    0,    ID_CHAR,
  225. /*   W */    parseIdent,    0,    ID_CHAR,
  226. /*   X */    parseIdent,    0,    ID_CHAR,
  227. /*   Y */    parseIdent,    0,    ID_CHAR,
  228. /*   Z */    parseIdent,    0,    ID_CHAR,
  229. /*   [ */    0,        OPEN_BRACKET, 0,
  230. /*   \ */    parseBinOP,    0,    SPECIAL_CHAR,
  231. /*   ] */    0,        CLOSE_BRACKET, 0,
  232. /*   ^ */    0,        UPARROW, 0,
  233. /*   _ */    0,        ASSIGN,    0,
  234. /*   ` */    illegal,    0,    0,
  235. /*   a */    parseIdent,    0,    ID_CHAR,
  236. /*   b */    parseIdent,    0,    ID_CHAR,
  237. /*   c */    parseIdent,    0,    ID_CHAR,
  238. /*   d */    parseIdent,    0,    ID_CHAR,
  239. /*   e */    parseIdent,    0,    ID_CHAR,
  240. /*   f */    parseIdent,    0,    ID_CHAR,
  241. /*   g */    parseIdent,    0,    ID_CHAR,
  242. /*   h */    parseIdent,    0,    ID_CHAR,
  243. /*   i */    parseIdent,    0,    ID_CHAR,
  244. /*   j */    parseIdent,    0,    ID_CHAR,
  245. /*   k */    parseIdent,    0,    ID_CHAR,
  246. /*   l */    parseIdent,    0,    ID_CHAR,
  247. /*   m */    parseIdent,    0,    ID_CHAR,
  248. /*   n */    parseIdent,    0,    ID_CHAR,
  249. /*   o */    parseIdent,    0,    ID_CHAR,
  250. /*   p */    parseIdent,    0,    ID_CHAR,
  251. /*   q */    parseIdent,    0,    ID_CHAR,
  252. /*   r */    parseIdent,    0,    ID_CHAR,
  253. /*   s */    parseIdent,    0,    ID_CHAR,
  254. /*   t */    parseIdent,    0,    ID_CHAR,
  255. /*   u */    parseIdent,    0,    ID_CHAR,
  256. /*   v */    parseIdent,    0,    ID_CHAR,
  257. /*   w */    parseIdent,    0,    ID_CHAR,
  258. /*   x */    parseIdent,    0,    ID_CHAR,
  259. /*   y */    parseIdent,    0,    ID_CHAR,
  260. /*   z */    parseIdent,    0,    ID_CHAR,
  261. /*   { */    illegal,    0,    0,
  262. /*   | */    parseBinOP,    0,    SPECIAL_CHAR,
  263. /*   } */    illegal,    0,    0,
  264. /*   ~ */    parseBinOP,    0,    SPECIAL_CHAR,
  265. /*  ^? */    illegal,    0,    0
  266. };
  267.  
  268.  
  269. int yylex(lvalp, llocp)
  270. YYSTYPE *lvalp;
  271. voidPtr *llocp;        /* Bison is broken, doesn define type YYLTYPE!! */
  272. {
  273.   int        ic, result, tokenStartPos;
  274.  
  275.   if (produceInternalToken) {
  276.     /* The internal token is a trick to make the grammar be "conditional".
  277.      * Normally, the allowable syntax for internal compilation is that of
  278.      * a single method (without the terminating BANG).  However, would make
  279.      * for an ambiguous grammar since a method declaration could look like
  280.      * an immediate expression.  So, when the compiler is called internally,
  281.      * we force the first token returned by the lexer to be INTERNAL_TOKEN
  282.      * and make the top most production in the grammar use INTERNAL_TOKEN as
  283.      * the first token of an internal method. */
  284.     produceInternalToken = false;
  285.     return (INTERNAL_TOKEN);
  286.   }
  287.  
  288.   while ((ic = nextChar()) != EOF) {
  289.     if ((charTab[ic].charClass & WHITE_SPACE) == 0) {
  290.       if (methodStartPos < 0) {
  291.     tokenStartPos = getCurFilePos();
  292.       }
  293.  
  294.       if (charTab[ic].lexFunc) {
  295.     result = (*charTab[ic].lexFunc)(ic, lvalp);
  296.     if (result) {
  297.       if (methodStartPos < 0) { /* only do this here to ignore comments */
  298.         methodStartPos = tokenStartPos - 1;
  299.       }
  300.       return (result);
  301.     }
  302.       } else if (charTab[ic].retToken) {
  303.     return (charTab[ic].retToken);
  304.       } else {
  305.     errorf("Unknown character %d in input stream, ignoring", ic);
  306.     hadError = true;
  307.       }
  308.     }
  309.   }
  310.   return (0);            /* keep fussy compilers happy */
  311. }
  312.  
  313. initLexer(calledInternally)
  314. Boolean calledInternally;
  315. {
  316.   byteAddr = 0;
  317.   
  318.   produceInternalToken = calledInternally;
  319. }
  320.  
  321. static int illegal(c, lvalp)
  322. char    c;
  323. YYSTYPE *lvalp;
  324. {
  325.   char        charName[3], *cp;
  326.  
  327.   cp = charName;
  328.  
  329.   if (c < ' ' || c > '~') {
  330.     *cp++ = '^';
  331.     c &= 127;            /* strip high bit */
  332.     c ^= 64;            /* uncontrolify */
  333.   }
  334.  
  335.   *cp++ = c;
  336.   *cp++ = '\0';
  337.   
  338.   errorf("Illegal character %s", charName);
  339.   hadError = true;
  340. }
  341.  
  342.  
  343. /*
  344.  *    static int comment(c, lvalp)
  345.  *
  346.  * Description
  347.  *
  348.  *    Scan a comment, but return 0 to indicate to the lexer that it's to be
  349.  *    ignored (since it is a comment).
  350.  *
  351.  * Inputs
  352.  *
  353.  *    c     : first character of the comment (the delimiter).  Not terribly
  354.  *        useful, but it's what the lexer calls us with.
  355.  *    lvalp : ignored...passed because we're invoked indirectly and some of
  356.  *        the functions that could be called require this parameter.
  357.  *
  358.  * Outputs
  359.  *
  360.  *    0, which tells the lexer to ignore this token.
  361.  */
  362. static int comment(c, lvalp)
  363. char    c;
  364. YYSTYPE *lvalp;
  365. {
  366.   scanStringoid(c, "comment");
  367.  
  368.   return (0);
  369. }
  370.  
  371. /*
  372.  *    static int charLiteral(c, lvalp)
  373.  *
  374.  * Description
  375.  *
  376.  *    parse a character literal.
  377.  *
  378.  * Inputs
  379.  *
  380.  *    c     : input character -- ignored
  381.  *    lvalp : ignored -- passed because we're called indirectly.
  382.  *
  383.  * Outputs
  384.  *
  385.  *    CHAR_LITERAL token normally; 0 on EOF.
  386.  */
  387. static int charLiteral(c, lvalp)
  388. char    c;
  389. YYSTYPE *lvalp;
  390. {
  391.   int        ic;
  392.  
  393.   ic = nextChar();
  394.   if (ic == EOF) {
  395.     errorf("Unterminated character literal, attempting recovery");
  396.     unreadChar(ic);
  397.     hadError = true;
  398.     return (0);
  399.   } else {
  400.     lvalp->cval = ic;
  401.     return (CHAR_LITERAL);
  402.   }
  403. }
  404.  
  405. static int parseColon(c, lvalp)
  406. char    c;
  407. YYSTYPE *lvalp;
  408. {
  409.   int        ic;
  410.  
  411.   ic = nextChar();
  412.   if (ic == '=') {        /* parse :=, compatibility mode assign */
  413.     return (ASSIGN);
  414.   } else {
  415.     unreadChar(ic);
  416.   }
  417.  
  418.   return (COLON);
  419. }
  420.  
  421.  
  422. static int parseBinOP(c, lvalp)
  423. char    c;
  424. YYSTYPE *lvalp;
  425. {
  426.   char        buf[3], *bp;
  427.   int        ic;
  428.  
  429.   bp = buf;
  430.   *bp++ = c;
  431.  
  432.   ic = nextChar();
  433.   if (c == '<') {
  434.     if (ic == 'p') {
  435.       if (parsePrimitive(ic, lvalp)) {
  436.     return (PRIMITIVE_START);
  437.       }
  438.     }
  439.   }
  440.   if (c == '-') {
  441.     unreadChar(ic);
  442.     if (isDigit(ic)) {
  443.       return (parseNumber('-', lvalp));
  444.     }
  445.   } else {
  446.     if (ic != EOF && (charTab[ic].charClass & SPECIAL_CHAR)) {
  447.       *bp++ = ic;
  448.     } else {
  449.       unreadChar(ic);
  450.     }
  451.   }
  452.  
  453.   *bp = '\0';
  454.  
  455.   if (strcmp(buf, "!") == 0) {
  456.     /* technically, token BANG has no string value, it's just a simple token,
  457.        so we return from here before we set the token's value */
  458.     return (BANG);
  459.   } else if (strcmp(buf, "!!") == 0) {
  460.     unreadChar('!');
  461.     return (BANG);
  462.   }
  463.   
  464.   lvalp->sval = copyStr(buf);
  465.   
  466.   if (strcmp(buf, "|") == 0) {
  467.     return (VERTICAL_BAR);
  468.   } else {
  469.     return (BINOP);
  470.   }
  471. }
  472.  
  473. static int stringLiteral(c, lvalp)
  474. char    c;
  475. YYSTYPE *lvalp;
  476. {
  477.   lvalp->sval = copyStr(scanStringoid(c, "string"));
  478.   return (STRING_LITERAL);
  479. }
  480.  
  481. static Boolean parsePrimitive(c, lvalp)
  482. char    c;
  483. YYSTYPE *lvalp;
  484. {
  485.   Boolean     result;
  486.  
  487.   parseIdent(c, lvalp);
  488.   result = (strcmp(lvalp->sval, "primitive:") == 0);
  489.   free(lvalp->sval);
  490.   return (result);
  491. }
  492.  
  493. static int parseIdent(c, lvalp)
  494. char    c;
  495. YYSTYPE *lvalp;
  496. {
  497.   int        ic, identType;
  498.   
  499.   initIdent(c);
  500.   
  501.   identType = IDENTIFIER;
  502.   
  503.   for (;;) {
  504.     while (((ic = nextChar()) != EOF) && (charTab[ic].charClass & ID_CHAR)) {
  505.       addIdentChar(ic);
  506.     }
  507.     
  508.     if (ic == ':') {        /* we have a keyword if id ends with : */
  509.       addIdentChar(ic);
  510.       ic = nextChar();
  511.       unreadChar(ic);
  512.       if (ic == EOF || (charTab[ic].charClass & ID_CHAR) == 0
  513.       || (charTab[ic].charClass & DIGIT) != 0) {
  514.     if (identType == IDENTIFIER) {
  515.       /* first time through */
  516.       identType = KEYWORD;
  517.     }
  518.     break;
  519.       }
  520.       identType = SYMBOL_KEYWORD;
  521.     } else {
  522.       unreadChar(ic);
  523.       break;
  524.     }
  525.   }
  526.   
  527.   lvalp->sval = copyStr(doneIdent());
  528.   
  529.   return (identType);
  530. }
  531.  
  532. static int parseNumber(c, lvalp)
  533. char    c;
  534. YYSTYPE *lvalp;
  535. {
  536.   int        base, exponent, ic;
  537.   double    num, sign, floatExponent;
  538.   Boolean    mantissaParsed = false, isNegative = false, dotSeen = false;
  539.   double scale;
  540.   
  541.   base = 10;
  542.   exponent = 0;
  543.   scale = 1;
  544.   ic = c;
  545.  
  546.   if (ic != '-') {
  547.     parseDigits(ic, 10, &num);
  548.     ic = nextChar();
  549.     if (ic == 'r') {
  550.       base = num;
  551.       ic = nextChar();
  552.     } else {
  553.       mantissaParsed = true;
  554.     }
  555.   }
  556.  
  557.   /*
  558.    * here we've either
  559.    *  a) parsed base, an 'r' and are sitting on the following character
  560.    *  b) parsed the integer part of the mantissa, and are sitting on the char
  561.    *     following it, or
  562.    *  c) parsed nothing and are sitting on a - sign.
  563.    */
  564.   if (!mantissaParsed) {
  565.     if (ic == '-') {
  566.       isNegative = true;
  567.       ic = nextChar();
  568.     }
  569.     
  570.     parseDigits(ic, base, &num);
  571.     ic = nextChar();
  572.   }
  573.  
  574.   if (ic == '.') {
  575.     ic = nextChar();
  576.     if (!isDigit(ic)) {
  577.       /* OOPS...we gobbled the '.' by mistake...it was a statement boundary
  578.      delimiter.  We have an integer that we need to return, and need to
  579.      push back both the . and the character that we just read. */
  580.       unreadChar(ic);
  581.       ic = '.';
  582.     } else {
  583.       dotSeen = true;
  584.       parseFraction(ic, base, &num, &scale);
  585.       ic = nextChar();
  586.     }
  587.   }
  588.  
  589.   if (ic == 'e') {
  590.     ic = nextChar();
  591.     if (ic == '-') {
  592.       parseDigits(nextChar(), 10, &floatExponent);
  593.       exponent -= floatExponent;
  594.     } else {
  595.       parseDigits(ic, 10, &floatExponent);
  596.       exponent += floatExponent;
  597.     }
  598.   } else {
  599.     unreadChar(ic);
  600.   }
  601.  
  602.   if (scale != 0.0) {
  603.     num *= scale/*pow((double)base, (double)exponent)*/;
  604.   }
  605.  
  606.   if (exponent != 0) {
  607.     num *= pow((double)base, (double)exponent);
  608.   }
  609.  
  610.   if (isNegative) {
  611.     num = -num;
  612.   }
  613.     
  614.   if (dotSeen) {
  615.     lvalp->fval = num;
  616.     return (FLOATING_LITERAL);
  617.   } else {
  618.     lvalp->ival = num;
  619.     if (num < -(1<<30) || num >= (1 << 30)) {
  620.       /* at least warn the guy... */
  621.       errorf("Integer literal %d too large to be represented in Smalltalk",
  622.          num);
  623.       hadError = true;
  624.     }
  625.       
  626.     return (INTEGER_LITERAL);
  627.   }
  628. }
  629.  
  630. static Boolean parseDigits(c, base, numPtr)
  631. char    c;
  632. int    base;
  633. double    *numPtr;
  634. {
  635.   double    result;
  636.   Boolean    oneDigit = false;
  637.  
  638.   for (result = 0.0; isBaseDigit(c, base); c = nextChar()) {
  639.     result *= base;
  640.     oneDigit = true;
  641.     result += digitToInt(c, base);
  642.   }
  643.  
  644.   if (!oneDigit) {
  645.     errorf("Unexpected EOF while scanning number");
  646.     hadError = true;
  647.   }
  648.  
  649.   unreadChar(c);
  650.  
  651.   *numPtr = result;
  652.  
  653.   return (true);
  654. }
  655.  
  656. static Boolean parseFraction(c, base, numPtr, scalePtr)
  657. char    c;
  658. int    base;
  659. double    *numPtr, *scalePtr;
  660. {
  661.   double    scale;
  662.   double    num;
  663.  
  664.   scale = 1.0;
  665.  
  666.   for (num = *numPtr; isBaseDigit(c, base); c = nextChar()) {
  667.     num *= base;
  668.     num += digitToInt(c, base);
  669.     scale /= base;
  670.   }
  671.  
  672.   unreadChar(c);
  673.  
  674.   *numPtr = num;
  675.   *scalePtr = scale;
  676.  
  677.   return (true);
  678. }
  679.  
  680.  
  681. int digitToInt(c, base)
  682. char    c;
  683. int    base;
  684. {
  685.   if (c < '0' || (c > '9' && c < 'A') || c > 'Z') {
  686.     errorf("Illegal digit %c in number", c);
  687.     hadError = true;
  688.     return (0);
  689.   }
  690.  
  691.   if (c >= 'A') {
  692.     c = c - 'A' + 10;
  693.   } else {
  694.     c -= '0';
  695.   }
  696.  
  697.   if (c >= base) {
  698.     errorf("Digit '%c' too large for base %d", c, base);
  699.     hadError = true;
  700.     return (0);
  701.   }
  702.  
  703.   return (c);
  704. }
  705.  
  706. static Boolean isBaseDigit(c, base)
  707. char    c;
  708. int    base;
  709. {
  710.   if (c < '0' || (c > '9' && c < 'A') || c > 'Z') {
  711.     return (false);
  712.   }
  713.   
  714.   if (c >= 'A') {
  715.     c = c - 'A' + 10;
  716.   } else {
  717.     c -= '0';
  718.   }
  719.  
  720.   return (c < base);
  721. }
  722.  
  723.  
  724. static Boolean isDigit(c)
  725. char    c;
  726. {
  727.   return ((charTab[c].charClass & DIGIT) != 0);
  728. }
  729.  
  730. static char *scanStringoid(startChar, typeName)
  731. char    startChar, *typeName;
  732. {
  733.   int        ic;
  734.  
  735.   initStrBuf();
  736.  
  737.   for (;;) {
  738.     ic = nextChar();
  739.     if (ic == EOF) {
  740.       errorf("Unterminated %s, attempting recovery", typeName);
  741.       hadError = true;
  742.       return (curStrBuf());
  743.     }
  744.     if (ic == startChar) {
  745.       /* check for doubled delimiters */
  746.       ic = nextChar();
  747.       if (ic != startChar) {
  748.     unreadChar(ic);
  749.     return (curStrBuf());
  750.       }
  751.     }
  752.     addStrBufChar(ic);
  753.   }
  754.   
  755. }
  756.  
  757. /*
  758.  * Report an error to the user.  ### Will show position in the text of
  759.  * the error at some point.
  760.  */
  761. errorf(str, ERROR_ARGS)
  762. char    *str;
  763. int    ERROR_ARGS;
  764. {
  765.   fflush(stdout);
  766.   lineStamp();
  767.   fprintf(stderr, str, ERROR_ARGS);
  768.   fprintf(stderr, "\n");
  769.   fflush(stderr);
  770. }
  771.  
  772.  
  773. yyerror(s)
  774. char    *s;
  775. {
  776.   lineStamp();
  777.   fprintf(stderr, "%s\n", s);
  778. }
  779.  
  780. static void lineStamp()
  781. {
  782.   if (inStream) {
  783.     if (inStream->fileName) {
  784.       fprintf(stderr, "\"%s\", ", inStream->fileName);
  785.     }
  786.     fprintf(stderr, "line %d: ", inStream->line);
  787.   } else {
  788.     fprintf(stderr, "<unknown position> ");
  789.   }
  790. }
  791.  
  792. StreamType getCurStreamType()
  793. {
  794.   if (inStream) {
  795.     return (inStream->type);
  796.   } else {
  797.     return (unknownStreamType);
  798.   }
  799. }
  800.  
  801. OOP getCurString()
  802. {
  803.   if (inStream && inStream->type == stringStreamType) {
  804.     return (stringNew(inStream->st_str.strBase));
  805.   } else {
  806.     return (nilOOP);
  807.   }
  808. }
  809.  
  810. OOP getCurFileName()
  811. {
  812.   if (inStream && inStream->type == fileStreamType) {
  813.     return (stringNew(inStream->fileName));
  814.   } else {
  815.     return (nilOOP);
  816.   }
  817. }
  818.  
  819. #ifdef USE_READLINE
  820. OOP getCurReadline()
  821. {
  822.   if (inStream && inStream->type == readlineStreamType) {
  823.     return (stringNew(inStream->st_str.strBase));
  824.   } else {
  825.     return (nilOOP);
  826.   }
  827. }
  828. #endif /* USE_READLINE */
  829.  
  830. int getMethodStartPos()
  831. {
  832.   return (methodStartPos);
  833. }
  834.  
  835. void clearMethodStartPos()
  836. {
  837.   methodStartPos = -1;
  838. }
  839.  
  840. int getCurFilePos()
  841. {
  842.   if (inStream && inStream->type == fileStreamType) {
  843.     return (ftell(inStream->st_file));
  844.   } else {
  845.     return (-1);
  846.   }
  847. }
  848.  
  849. static int nextChar()
  850. {
  851.   register int        ic;
  852.  
  853.   if (inStream->pushedBackCount > 0) {
  854.     ic = inStream->pushedBackChars[--inStream->pushedBackCount];
  855.     return (ic);
  856.   } else {
  857.     if (inStream->column == 0 && inStream->prompt) {
  858.       if (emacsProcess) {
  859.     printf("%c", EMACS_PROCESS_MARKER);
  860.       }
  861.       printf("st> ");
  862.     }
  863.     ic = myGetC(inStream);
  864.  
  865.     /* byteAddr++; */
  866.     if (ic == '\n') {        /* a new line that was not pushed back */
  867.       inStream->line++;
  868.       inStream->column = 0;
  869.     } else {
  870.       inStream->column++;
  871.     }
  872.     return (ic);
  873.   }
  874. }
  875.  
  876. /*
  877.  *    static void unreadChar(ic)
  878.  *
  879.  * Description
  880.  *
  881.  *    Push character 'ic' back into the input queue.  Allows for two
  882.  *    character pushback currently.  This solves the problem of lexing 3. and
  883.  *    then finding out that what we should have lexed was 3 followed by . as
  884.  *    a statement terminator.
  885.  *
  886.  * Inputs
  887.  *
  888.  *    ic    : character to push back into the input stream.
  889.  *
  890.  */
  891. static void unreadChar(ic)
  892. int    ic;
  893. {
  894.   inStream->pushedBackChars[inStream->pushedBackCount++] = ic;
  895. }
  896.  
  897.  
  898.  
  899. /***********************************************************************
  900.  *
  901.  *    Generic "stream" interface.  A stream is an abstraction for input and
  902.  *    output.  It is most like common lisp streams.  Basically, these streams
  903.  *    provide transparent reading from either a Smalltalk string, or a UNIX
  904.  *    file.  They stack, and the previous stream can be restored by doing a
  905.  *    "popStream" which optionally "closes" the current stream and goes back 
  906.  *    to using the previous stream.
  907.  *
  908.  *     The `readline()' interface:
  909.  *
  910.  *         The behavior is like the Smalltalk-String interface.
  911.  *         The end-of-string or a NULL strBase-pointer decides
  912.  *         to read in a new line. The prompt is still shown by
  913.  *         the readline() call.
  914.  *         
  915.  ***********************************************************************/
  916.  
  917. void popStream(closeIt)
  918. Boolean closeIt;
  919. {
  920.   Stream    oldStream;
  921.  
  922.   oldStream = inStream;
  923.   inStream = inStream->prevStream;
  924.  
  925.   if (closeIt && oldStream) {
  926.     myClose(oldStream);
  927.     free(oldStream);
  928.   }
  929. }
  930.  
  931. void pushUNIXFile(file, fileName)
  932. FILE    *file;
  933. char    *fileName;
  934. {
  935.   Stream    newStream;
  936.  
  937.   newStream = pushNewStream(fileStreamType);
  938.  
  939.   newStream->st_file = file;
  940.   newStream->fileName = fileName;
  941.   newStream->prompt = isatty(fileno(file));
  942. }
  943.  
  944. void pushSmalltalkString(stringOOP)
  945. OOP    stringOOP;
  946. {
  947.   Stream    newStream;
  948.  
  949.   newStream = pushNewStream(stringStreamType);
  950.  
  951.   newStream->st_str.strBase = (char *)toCString(stringOOP);
  952.   newStream->st_str.str = newStream->st_str.strBase;
  953.   newStream->fileName = "a Smalltalk string";
  954.   newStream->prompt = false;
  955. }
  956.  
  957. #ifdef USE_READLINE
  958. void pushReadlineString()
  959. {
  960.   Stream    newStream;
  961.  
  962.   newStream = pushNewStream(readlineStreamType);
  963.  
  964.   newStream->st_str.strBase = 0;    /* force readline() but no free() */
  965.   newStream->st_str.str = 0;
  966.   newStream->fileName = "a Readline string";
  967.   newStream->prompt = false;        /* prompt is shown by readline() */
  968. }
  969. #endif /* USE_READLINE */
  970.  
  971. static Stream pushNewStream(type)
  972. StreamType type;
  973. {
  974.   Stream    newStream;
  975.  
  976.   newStream = (Stream)malloc(sizeof(struct StreamStruct));
  977.  
  978.   newStream->pushedBackCount = 0;
  979.   newStream->line = 1;
  980.   newStream->column = 0;
  981.   newStream->type = type;
  982.   newStream->prevStream = inStream;
  983.   inStream = newStream;
  984.  
  985.   return (newStream);
  986. }
  987.  
  988.  
  989. static int myGetC(stream)
  990. Stream    stream;
  991. {
  992.   int        ic;
  993.  
  994.   if (stream->type == stringStreamType) {
  995.     ic = *stream->st_str.str++;
  996.     return ((ic == '\0') ? EOF : ic);
  997.   } else if (stream->type == fileStreamType) {
  998.     return (getc(stream->st_file));
  999. #ifdef USE_READLINE
  1000.   } else if (stream->type == readlineStreamType) {
  1001.     char *r_line;
  1002.     extern char *realloc();
  1003.     int r_len;
  1004.  
  1005.     if (stream->st_str.strBase) {
  1006.       ic = *stream->st_str.str++;
  1007.     } else {
  1008.       ic = '\0';
  1009.     }
  1010.  
  1011.     if (ic == '\0') {
  1012.       if(stream->st_str.strBase) {
  1013.     free(stream->st_str.strBase);
  1014.     stream->st_str.strBase = 0;
  1015.       }
  1016.       r_line = readline("st> ");
  1017.       if (!r_line) {
  1018.     /*
  1019.      * return value of NULL indicates EOF:
  1020.      */
  1021.     return EOF;
  1022.       }
  1023.       if (*r_line) {
  1024.     /*
  1025.      * add only non-empty lines.
  1026.      */
  1027.     add_history(r_line);
  1028.       }
  1029.       /*
  1030.        * tack on the newline, not returned by readline() :
  1031.        */
  1032.       r_len =  strlen(r_line);
  1033.       r_line = realloc(r_line, (unsigned) (r_len + 2));
  1034.       if (!r_line) {
  1035.     errorf("Out of memory, reallocating linebuffer space");
  1036.     stream->st_str.str = stream->st_str.strBase = 0;
  1037.     ic = '\n';            /* return a newline ... */
  1038.       } else {
  1039.     r_line[r_len] = '\n';
  1040.     r_line[r_len+1] = '\0';
  1041.     stream->st_str.str = stream->st_str.strBase = r_line;
  1042.     ic = *stream->st_str.str++;
  1043.       }
  1044.     }
  1045.     return (ic);
  1046. #endif /* USE_READLINE */
  1047.   } else {
  1048.     errorf("Bad stream type passed to myGetC");
  1049.     hadError = true;
  1050.   }
  1051. }
  1052.  
  1053. static void myClose(stream)
  1054. Stream    stream;
  1055. {
  1056.   if (stream->type == stringStreamType) {
  1057.     free(stream->st_str.strBase);
  1058.   } else if (stream->type == fileStreamType) {
  1059.     fclose(stream->st_file);
  1060. #ifdef USE_READLINE
  1061.   } else if (stream->type == readlineStreamType) {
  1062.     if (stream->st_str.strBase) {
  1063.       free(stream->st_str.strBase);
  1064.       stream->st_str.strBase = 0;
  1065.     }
  1066. #endif /* USE_READLINE */
  1067.   } else {
  1068.     errorf("Bad stream type passed to myClose");
  1069.     hadError = true;
  1070.   }
  1071. }
  1072.